function TextInput_(){
    this.$initEventable();
    this.$initRenderable();
    this.m_focusElm = null;
    this.m_readOnly = false;
    this.m_inComposition = false;
}

TextInput_.prototype = {
    _TPL_ELM: '<div id="#{id}_wrapper" tabindex="-1" style="position:absolute;left:0;top:0;width:0;height:0;overflow:hidden;">' +
        '<textarea id="#{id}_input" style="width:1px;height:1px;"></textarea>' +
        '</div>',
    $wrapperElm: function (){
        return this.$elm('wrapper');
    },
    $inputElm: function (){
        return this.$elm('input');
    },
    $renderHTML: function (containerElm){
        return Utils.format(this._TPL_ELM, { id: this.$id() });
    },
    $postRender: function (){
        var wrapperElm = this.$wrapperElm();
        var inputElm = this.$inputElm();
        if ('oninput' in inputElm) {
            this.$on(inputElm, 'input', this._onTextInput);
        } else if ('onpropertychange' in inputElm) {
            this.$on(inputElm, 'propertychange', this._onPropertyChange);
        }
        if (Utils.isOldIE) {
            this.$on(inputElm, 'keyup', this._onTextKeyUp);
        } else {
            this.$on(inputElm, 'compositionstart', this._onCompositionStart);
            this.$on(inputElm, 'compositionend', this._onCompositionEnd);
        }
        this.$on(wrapperElm, 'keydown', this._onKeyDown);
        this.$on(wrapperElm, 'keyup', this._onKeyUp);
        this._updateFocusElm();
    },
    inComposition: function (){
        return this.m_inComposition;
    },
    _onTextInput: function (evt){
        var self = this;
        setTimeout(function (){
            if (!self.inComposition()) {
                self.sendText();
            }
        });
    },
    _onPropertyChange: function (){
        if (Utils.isOldIE && this.$inputElm().value.charAt(0) > 128) return;
        var self = this;
        setTimeout(function (){
            if (!self.inComposition()) {
                self.sendText(null);
            }
        });
    },
    _onCompositionStart: function (){
        this.m_inComposition = true;
    },
    _onCompositionEnd: function (){
        this.m_inComposition = false;
    },
    _onTextKeyUp: function (evt){
        var self = this;
        if (this.inComposition()) {
            if (!this.$inputElm().value || evt.keyCode == 13 || evt.keyCode == 27) {
                setTimeout(function (){
                    self._onCompositionEnd();
                });
            }
        }
        if ((this.$inputElm().value.charCodeAt(0)|0) > 128) {
            this._onCompositionStart();
        }
        DOMUtils.preventDefault(evt);
    },
    _onKeyDown: function (evt){
        this.dispatchEvent(evt);
    },
    _onKeyUp: function (evt){
        this.dispatchEvent(evt);
    },
    _onFocus: function (evt){
        if (evt.target === this.m_focusElm) {
            this.dispatchEvent({ type: 'focus' });
        }
    },
    _onBlur: function (evt){
        if (evt.target === this.m_focusElm) {
            this.dispatchEvent({ type: 'blur' });
        }
    },
    _updateFocusElm: function (){
        var alreadyFocused = this.isFocused();
        var oldFocusElm = this.m_focusElm;
        this.m_focusElm = this.readOnly() ? this.$wrapperElm() : this.$inputElm();
        if (this.m_focusElm !== oldFocusElm) {
            if (oldFocusElm != null) {
                this.$un(oldFocusElm, 'focus', this._onFocus);
                this.$un(oldFocusElm, 'blur', this._onBlur);
            }
            this.$on(this.m_focusElm, 'focus', this._onFocus);
            this.$on(this.m_focusElm, 'blur', this._onBlur);
            if (alreadyFocused) {
                this.focus();
            }
        }
    },
    readOnly: function (){
        return this.m_readOnly;
    },
    setReadOnly: function (readOnly){
        this.m_readOnly = readOnly;
        this._updateFocusElm();
    },
    setPosition: function (pos){
        this.$wrapperElm().style.left = pos.x + 'px';
        this.$wrapperElm().style.top = pos.y + 'px';
    },
    focus: function (){
        var self = this;
        self.m_focusElm.focus();
        setTimeout(function (){
            self.m_focusElm.focus();
        });
    },
    isFocused: function (){
        return this.m_focusElm && this.m_focusElm.ownerDocument.activeElement === this.m_focusElm;
    },
    blur: function (){
        if (this.isFocused()) {
            this.m_focusElm.blur();
        }
    },
    sendText: function (text){
        var inputElm = this.$inputElm();
        if (text == null) {
            text = inputElm.value;
            if (text.charCodeAt(text.length - 1) == 0) {
                text = text.slice(0, -1);
            }
        }
        if (text && !this.readOnly()) {
            this.dispatchEvent({ type: 'textinput', text: text });
        }
        inputElm.value = '\0';
        try {
            inputElm.select();
        } catch(ex){}
    }
};

Utils.extend(TextInput_, Eventable);
Utils.extend(TextInput_, Renderable);